﻿using CRL.Attribute;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml.Linq;
namespace CRL
{
    /// <summary>
    /// 对象结构信息导出
    /// </summary>
    class SummaryAnalysis
    {
        #region obj
        class ObjItem
        {
            public string Name;
            public string Remark;
            public List<FieldItem> Fields = new List<FieldItem>();
            public void Add(FieldItem item)
            {
                Fields.Add(item);
            }
        }
        class FieldItem
        {
            public string Remark;
            public string Name;
            public Type Type;
            public override string ToString()
            {
                return string.Format("{0} {1}", Name, Remark);
            }
        }
        #endregion
        #region inner

        //private static bool CheckIfAnonymousType(Type type)
        //{
        //    if (type == null)
        //        throw new ArgumentNullException("type");

        //    return type.Name.Contains("AnonymousType")|| (type.Name.Contains("<>"));
        //}

        static List<Type> GetInfoFromDll(Type[] assemblyTypes, Func<Type,bool> match, out List<string> xmlFiles)
        {
            List<Type> findTypes = new List<Type>();
            xmlFiles = new List<string>();
            var removeKeys = new List<string>() { "RequestModel", "ResponseModel" };
            Dictionary<string, List<FieldItem>> fileds = new Dictionary<string, List<FieldItem>>();
            foreach (var currentType in assemblyTypes)
            {
                #region 加载类型
                var assembyle = System.Reflection.Assembly.GetAssembly(currentType);
                var file = assembyle.CodeBase.ToLower().Replace("file:///", "").Replace("/","\\").Replace(".dll", ".xml");
                if (!System.IO.File.Exists(file))
                {
                    //continue;
                    throw new Exception(file + " 注释文件不存在");
                }
                xmlFiles.Add(file);
                var types = assembyle.GetTypes().ToList();
                if (match != null)
                {
                    types = types.FindAll(b => match(b));
                }
                foreach (var type in types)
                {
                    if(Base.CheckIfAnonymousType(type))
                    {
                        continue;
                    }
                    bool remove = false;
                    foreach (var k in removeKeys)
                    {
                        if (type.FullName.Contains(k))
                        {
                            remove = true;
                        }
                    }
                    if (!remove && type.IsClass)
                    {
                        findTypes.Add(type);
                    }
                }
                #endregion
            }
            return findTypes.OrderBy(b => b.Name).ToList();
        }
        static Dictionary<string, ObjItem> GetInfoFromXml(List<string> xmlFiles)
        {
            Dictionary<string, ObjItem> objItems = new Dictionary<string, ObjItem>();
            if (xmlFiles == null)
            {
                return objItems;
            }
            if (xmlFiles.Count == 0)
            {
                return objItems;
            }
            foreach (string xmlFile in xmlFiles)
            {
                var rootE = XElement.Load(xmlFile);
                //找对象注释
                IEnumerable<XElement> query2 =
                                            from ele in rootE.Element("members").Elements("member")
                                            where ele.Attribute("name").Value.StartsWith("T:")
                                            select ele;
                foreach (XElement e in query2)
                {
                    string name = e.Attribute("name").Value.Substring(2);
                    var summary = e.Element("summary");
                    string remark = "";
                    if (summary != null)
                    {
                        remark = summary.Value.Trim();
                    }
                    if (!objItems.ContainsKey(name))
                    {
                        objItems.Add(name, new ObjItem() { Name = name, Remark = remark });
                    }
                }
                //属性
                IEnumerable<XElement> query =
                                            from ele in rootE.Element("members").Elements("member")
                                            where ele.Attribute("name").Value.StartsWith("P:")
                                            select ele;
                foreach (XElement e in query)
                {
                    string name = e.Attribute("name").Value.Substring(2);
                    int index = name.LastIndexOf('.');
                    string propertyName = name.Substring(index + 1, name.Length - index - 1);
                    string typeName = name.Substring(0, index);
                    var summary = e.Element("summary");
                    string remark = "";
                    if (summary != null)
                    {
                        remark = summary.Value.Trim();
                    }
                    if (!objItems.ContainsKey(typeName))
                    {
                        objItems.Add(typeName, new ObjItem() { Name = typeName });
                    }
                    objItems[typeName].Add(new FieldItem() { Name = propertyName, Remark = remark });
                }
                
            }
            var list2 = new List<FieldItem>();
            list2.Add(new FieldItem() { Name = "Id", Type = typeof(Int32), Remark = "自增主键" });
            list2.Add(new FieldItem() { Name = "AddTime", Type = typeof(DateTime), Remark = "添加时间" });
            objItems.Add("IModelBase", new ObjItem() { Name = "IModelBase", Remark = "", Fields = list2 });
            return objItems;
        }

        static Dictionary<Type, TableInnerAttribute> Merge(List<Type> types,Dictionary<string, ObjItem> objItems)
        {
            var result = new Dictionary<Type, TableInnerAttribute>();
            foreach(var type in types)
            {
                string typeName = type.FullName;
                var table = TypeCache.GetTable(type);
                var fields = table.FieldsDic;
                //var table = new TableInnerAttribute();
                //table.Type = type;
                //table.TableName = tableBase.TableName;
                if (objItems.ContainsKey(typeName))
                {
                    var objItem = objItems[typeName];
                    table.Remark = objItem.Remark;
                    Type parentType = type.BaseType;
                    while (parentType != typeof(Object))
                    {
                        if (objItems.ContainsKey(parentType.FullName))
                        {
                            objItem.Fields.AddRange(objItems[parentType.FullName].Fields);
                        }
                        parentType = parentType.BaseType;
                    }
         
                    foreach (var item in fields.Values)
                    {
                        var item2 = objItem.Fields.Find(b => b.Name == item.MemberName);
                        if (item2 != null)
                        {
                            item.Remark = item2.Remark;
                        }
                    }
                }
                else
                {
                    objItems.Remove(typeName);
                }
                //table.Fields = fields.Values.ToList();
                result.Add(type, table);
            }
            return result;
        }
        #endregion
        static Dictionary<Type, Dictionary<Type, TableInnerAttribute>> caches = new Dictionary<Type, Dictionary<Type, TableInnerAttribute>>();
        internal static Dictionary<Type, TableInnerAttribute> GetFieldComments(Type assemblyType)
        {
            var a2 = caches.TryGetValue(assemblyType,out var tables);
            if (!a2)
            {
                var a = GetInfoFromDll(new Type[] { assemblyType }, null, out var xmlFiles);
                var b = GetInfoFromXml(xmlFiles);
                tables = Merge(a, b);
                caches.Add(assemblyType, tables);
            }
            return tables;
        }
    }

    public class FieldRowItem
    {
        public string Name { get; set; }
        public string Type { get; set; }
        public string Remark { get; set; }
        public bool IsTable;
    }
}
